package collections.list.array_list;

import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;

import collections.list.MyList;

public class MyArrayList<T> implements MyList<T>, RandomAccess {

	public static final int DEFAULT_CAPACITY = 10;
	public static final double DEFAULT_CAPACITY_MULTIPLIER = 1.5;
	private Object[] array;
	private int capacity = DEFAULT_CAPACITY;
	private int size = 0;

	public MyArrayList(){
		array = new Object[capacity];
	}
	
	public MyArrayList(MyList<T> list){
		for(T element: list){
			add(element);
		}
	}
	
	public MyArrayList(int initialCapacity){
		capacity = initialCapacity;
		array = new Object[capacity];
	}
	
	@Override
	public Iterator<T> iterator() {
		Iterator<T> iterator = new Iterator<T>() {
			private int currentPosition = 0;
			@Override
			public boolean hasNext() {
				if(size == 0 || currentPosition >= size){
					return false;
				}			
				return true;
			}

			@Override
			public T next() {
				T result = (T)array[currentPosition];
				currentPosition++;
				return result;
			}

			@Override
			public void remove() {
				throw new UnsupportedOperationException();			
			}
		};
		return iterator;
	}

	@Override
	public void add(T element) {
		if(size == capacity){
			capacity = (int)(capacity * DEFAULT_CAPACITY_MULTIPLIER);
			Object[] temp = array;
			array = new Object[capacity];
			for(int i = 0; i < size; i++){
				array[i] = temp[i];
			}
			array[size] = (Object)element;
			size++;
		} else {
			array[size] = (Object)element;
			size++;
		}
	}

	@Override
	public void add(int index, T element) throws IndexOutOfBoundsException {
		if(index < 0) {
			throw new IllegalArgumentException();
		}
		if (index > size) {
			throw new IndexOutOfBoundsException();
		}
		if(size == capacity){
			capacity = (int)(capacity * DEFAULT_CAPACITY_MULTIPLIER);
			Object[] temp = array;
			array = new Object[capacity];
			for(int i = 0; i < index; i++){
				array[i] = temp[i];
			}
			for(int i = index; i < size; i++){
				array[i + 1] = temp[i];
			}
			array[index] = (Object)element;
			size++;
		} else {
			Object[] temp = new Object[array.length];
			for(int i = 0; i < array.length; i++){
				temp[i] = array[i];
			}
 			for(int i = index; i < size; i++){
				array[i + 1] = temp[i];
			}
			array[index] = (Object)element;
			size++;
		}
	}

	@Override
	public void addAll(T[] collection) {
		for(T element: collection){
			add(element);
		}
	}

	@Override
	public void AddAll(int index, T[] collection)
			throws IndexOutOfBoundsException {
		if(index < 0) {
			throw new IllegalArgumentException();
		}
		if (index > size) {
			throw new IndexOutOfBoundsException();
		}
		for(int i = 0; i < collection.length; i++){
			add(index + i, collection[i]);
		}
	}

	@Override
	public T get(int index) throws IndexOutOfBoundsException {
		if(index < 0) {
			throw new IllegalArgumentException();
		}
		if(index > size){
			throw new IndexOutOfBoundsException();
		}
		return (T)array[index];
	}

	@Override
	public T remove(int index) throws IndexOutOfBoundsException {
		if(index < 0) {
			throw new IllegalArgumentException();
		}
		if(index > size){
			throw new IndexOutOfBoundsException();
		}
		T result = (T)array[index];
		for(int i = index; i < size - 1; i++){
			array[i] = array[i + 1];
		}
		size--;
		return result;
	}

	@Override
	public void clear() {
		array = new Object[capacity];
		size = 0;
	}

	@Override
	public boolean isEmpty() {
		if(size == 0){
			return true;
		}
		return false;
	}

	@Override
	public void set(int index, T element) throws IndexOutOfBoundsException {
		if(index < 0) {
			throw new IllegalArgumentException();
		}
		if(index > size){
			throw new IndexOutOfBoundsException();
		}
		array[index] = element;
	}

	@Override
	public int indexOf(T element) throws NoSuchElementException {
		if(size == 0){
			throw new NoSuchElementException();
		}
		for(int i = 0; i < size; i++){
			T value = (T)array[i];
			if(value.equals(element)){
				return i;
			}
		}
		throw new NoSuchElementException();
	}

	@Override
	public int size() {
		return size;
	}

	@Override
	public T[] toArray() {
		if(array == null){
			return null;
		}
		Object obj = array[0];
		T[] result = (T[])Array.newInstance(obj.getClass(), size);
		for(int i = 0; i < size; i++){
			result[i] = (T)array[i];
		}
		return result;
	}

	public void ensureCapacity(int minCapacity) throws IllegalArgumentException {
		if(size > minCapacity){
			throw new IllegalArgumentException("Current size of collection is more then entered value!");
		}
		capacity = minCapacity;
		Object[] temp = array;
		array = new Object[capacity];
		for(int i = 0; i < size; i++){
			array[i] = temp[i];
		}
	}
	
	public void trimToSize(){
		capacity = size;
		Object[] temp = array;
		array = new Object[capacity];
		for(int i = 0; i < size; i++){
			array[i] = temp[i];
		}
	}
}
